package simulator.mv;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.util.LinkedList;
import java.util.Stack;

public class VisionProcesser {
	
	/**
	 * TODO include use of ERROR value...
	 * This function allows to get shapes from a given image.
	 * @param image
	 * @param targetColor
	 * @param error
	 * @return Set of shapes.
	 */
	public static LinkedList<ShapeBean> getShapes(BufferedImage image, Color targetColor,int error){
		LinkedList<ShapeBean> shapes = new LinkedList<ShapeBean>();
		int currentColorValue, targetColorValue = targetColor.getRGB();
		boolean[][]flag = new boolean[image.getHeight()][image.getWidth()];
		//Initializing flags within false value:
		for (int i = 0; i < flag.length; i++) {
			for (int j = 0; j < flag[i].length; j++) {
				flag[i][j]=false;
			}
		}
		//Parsing initial pixels:
		for (int y = 0; y < image.getHeight(); y++) {
			for (int x = 0; x < image.getWidth(); x++) {
				currentColorValue = image.getRGB(x, y);
				//Asking for current pixel whether is the searching pixel:
				/*if ((currentColorValue == targetColorValue) && (!flag[y][x])) {*/
				if (VisionProcesser.isIntoRGBTone(currentColorValue,targetColorValue,error) && (!flag[y][x])) {
					//Getting shape from current position:
					ShapeBean tmpShape = VisionProcesser.getShapes(image, new PixelBean(targetColor,new Point(x,y)), targetColor, flag, error);
					if(tmpShape!=null){
						shapes.add(tmpShape);
					}
				}
			}
		}
		return shapes;
	}
	/**
	 * This function allows to find the description of a shape, e.g. the bound.
	 * @param image
	 * @param startingPixel
	 * @param targetColor
	 * @param flag
	 * @param error
	 * @return A shape.
	 */
	private static ShapeBean getShapes(BufferedImage image, PixelBean startingPixel, Color targetColor, boolean[][]flag,int error){
		Stack<Point>positions = new Stack<Point>();
		Point p;
		final int WIDTH = image.getWidth(),HEIGHT = image.getHeight();
		int currentColorValue, targetColorValue = targetColor.getRGB();
		int xMin,xMax,yMin,yMax,x,y;
		//Adding starting point:
		positions.push(startingPixel.getPoint());
		xMin = xMax = (int)startingPixel.getPoint().getX();
		yMin = yMax = (int)startingPixel.getPoint().getY();
		while(!positions.empty()){
			p = positions.pop();
			x = (int)p.getX();
			y = (int)p.getY();
			if(x>=0 && y>=0 && x<WIDTH && y<HEIGHT){
				currentColorValue = image.getRGB(x,y);
				/*if ((currentColorValue == targetColorValue) && (!flag[y][x])) {*/
				if (VisionProcesser.isIntoRGBTone(currentColorValue,targetColorValue,error) && (!flag[y][x])) {
					//Flaging position as checked:
					flag[y][x] = true;
					//Adding neighbour pixels of current one to stack:
					positions.push(new Point(x,y+1));
					positions.push(new Point(x+1,y));
					positions.push(new Point(x,y-1));
					positions.push(new Point(x-1,y-1));
					//Updating mins and maxs for further bound calculation:
					xMin = (x<xMin)?x:xMin;
					yMin = (y<yMin)?y:yMin;
					xMax = (x>xMax)?x:xMax;
					yMax = (y>yMax)?y:yMax;
				}
			}
		}
		//Setting answer:
		ShapeBean shape = new ShapeBean();
		int w = xMax-xMin,h = yMax-yMin;
		shape.setRectangle(new Rectangle(new Point(xMin,yMin),new Dimension(w,h)));
		shape.setColor(targetColor);
		/*shape.setDimension(new Dimension(w,h));
		shape.setSample(startingPixel);*/
		shape.setDescription(new DescriptionBean(Color.WHITE.getRGB(),"W:"+w+"[px];\n H:"+h+"[px]."));
		return shape;
	}
	private static boolean isIntoRGBTone(int currentRGBTone,int targetRGBTone,int error){
		Color current = new Color(currentRGBTone);
		Color target = new Color(targetRGBTone);
		return 	VisionProcesser.isIntoRange(current.getRed(), target.getRed(), error) &&
				VisionProcesser.isIntoRange(current.getGreen(), target.getGreen(), error) &&
				VisionProcesser.isIntoRange(current.getBlue(), target.getBlue(), error);
	}
	/**
	 * This function allows to know whether a given current value is into a range given 
	 * by a target value and an error value.
	 * @param currentValue
	 * @param targetValue
	 * @param error
	 * @return Whether currentValue is into targetValue+error or targetValue-error.  
	 */
	private static boolean isIntoRange(int currentValue,int targetValue,int error){
		return (currentValue>=(targetValue-error))&&(currentValue<=(targetValue+error));
	}
}
